const shader = require('../shader/index');
const bezierLine = require('../util/bezierLine');
const lngLat2ThetaPhiXYZ = require('../util/lngLat2ThetaPhiXYZ').lngLat2ThetaPhiXYZ;
const initArrayBuffer = require('../util/initArrayBuffer');
const initIndexBuffer = require('../util/initIndexBuffer');
const texturePolygon = require('../util/texturePolygon');

const glMatrix = require('gl-matrix');
const mat4 = glMatrix.mat4;
const vec3 = glMatrix.vec3;
const vec4 = glMatrix.vec4;

module.exports = class DynamicLabelLayer {
    constructor(option) {
        // init config
        this.sizeAttenuation = option.sizeAttenuation;
        this.positions = option.positions;
        this.buffers = [];

        // init shader
        this.gl = option.gl;
        const gl = this.gl;
        this.radius = option.radius;
        this.segmentCount = option.segmentCount;
        this.speed = option.speed;

        this.timeNow = 0;
        this.count = 0;

        const program = gl.createProgram();
        this.program = program;
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, shader.dynamicLabel.fragmentSource);
        gl.compileShader(fragmentShader);
        gl.attachShader(program, fragmentShader);
        const vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, shader.dynamicLabel.vertexSource);
        gl.compileShader(vertexShader);
        gl.attachShader(program, vertexShader);
        gl.linkProgram(program);

        // get attribLocation
        this.attribLocation = {
            a_Position: gl.getAttribLocation(program, 'a_Position'),
            a_SideCoord: gl.getAttribLocation(program, 'a_SideCoord'),
            a_TexCoord: gl.getAttribLocation(program, 'a_TexCoord'),
        }

        this.uniformLocation = {
            u_ModelMatrix: gl.getUniformLocation(program, 'u_ModelMatrix'),
            u_ViewMatrix: gl.getUniformLocation(program, 'u_ViewMatrix'),
            u_ProjMatrix: gl.getUniformLocation(program, 'u_ProjMatrix'),
            u_Sampler: gl.getUniformLocation(program, 'u_Sampler'),
            u_Resolution: gl.getUniformLocation(program, 'u_Resolution'),
            u_Size: gl.getUniformLocation(program, 'u_Size'),
            u_CurrentAngle: gl.getUniformLocation(program, 'u_CurrentAngle'),
            u_SizeAttenuation: gl.getUniformLocation(program, 'u_SizeAttenuation'),
            u_Offset: gl.getUniformLocation(program, 'u_Offset'),
            u_Move: gl.getUniformLocation(program, 'u_Move'),
            u_IsShow: gl.getUniformLocation(program, 'u_IsShow')
        }

        // 经纬度，宽高（像素），偏移（像素），纹理类型，图片路径/文字内容，点对应球半径
        for (let i = 0; i < this.positions.length; i++) {
            const texturePolygonVertices = texturePolygon(this.positions[i].lngLat, this.radius);

            let texture = null;
            let start = null;
            let path = null;
            if (this.positions[i].type === 'text') {
                const textCanvas = makeTextCanvas(this.positions[i].content, 2 * this.positions[i].content.length * this.positions[i].fontSize, 2 * this.positions[i].fontSize, this.positions[i].fontSize);
                const textTex = gl.createTexture();
                gl.bindTexture(gl.TEXTURE_2D, textTex);
                gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textCanvas);
                // make sure we can render it even if it's not a power of 2
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
                gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
                texture = textTex;
            }
            else if (this.positions[i].type === 'image') {
                makeImageCanvas(this.positions[i].content, this.positions[i].size[0], this.positions[i].size[1], gl, this.buffers, i);
            }

            if (this.positions[i].movePath) {
                start = lngLat2ThetaPhiXYZ(this.positions[i].movePath[0], this.radius);
                path = bezierLine.apply(null, this.positions[i].movePath.slice(0, 4).concat([this.radius]));
            }

            this.buffers.push({
                positionVertexBuffer: initArrayBuffer(gl, new Float32Array(texturePolygonVertices.position)),
                texCoordVertexBuffer: initArrayBuffer(gl, new Float32Array(texturePolygonVertices.uv)),
                sideCoordVertexBuffer: initArrayBuffer(gl, new Float32Array(texturePolygonVertices.side)),
                indexBuffer: initIndexBuffer(gl, new Uint16Array(texturePolygonVertices.index)),
                texture: texture,
                n: texturePolygonVertices.index.length,
                size: this.positions[i].size ? this.positions[i].size : [this.positions[i].content.length * this.positions[i].fontSize + 4, this.positions[i].fontSize + 4],
                offset: this.positions[i].offset,
                start: start,
                path: path,
                startSeq: this.positions[i].movePath[4]
            });
        }
    }

    render(config) {
        const modelMatrix = config.modelMatrix;
        const viewMatrix = config.viewMatrix;
        const projMatrix = config.projMatrix;
        const normalMatrix = config.normalMatrix;
        const currentAngle = config.currentAngle;

        if (this.timeNow) {
            const time = Date.now();
            this.count += this.speed * (time - this.timeNow);
            this.timeNow = time;
        }
        else {
            this.timeNow = Date.now();
        }

        if (this.count > this.segmentCount) this.count = 0;

        const gl = this.gl;
        const windowWidth = gl.drawingBufferWidth;
        const windowHeight = gl.drawingBufferHeight;

        gl.useProgram(this.program);

        // gl.disable(gl.DEPTH_TEST);
        gl.enable(gl.BLEND);
        gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
        gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);

        gl.uniform1i(this.uniformLocation.u_Sampler, 0);

        gl.uniformMatrix4fv(this.uniformLocation.u_ModelMatrix, false, modelMatrix);
        gl.uniformMatrix4fv(this.uniformLocation.u_ViewMatrix, false, viewMatrix);
        gl.uniformMatrix4fv(this.uniformLocation.u_ProjMatrix, false, projMatrix);
        gl.uniform2f(this.uniformLocation.u_Resolution, windowWidth, windowHeight);
        gl.uniform2f(this.uniformLocation.u_CurrentAngle, currentAngle[0], currentAngle[1]);
        gl.uniform1f(this.uniformLocation.u_SizeAttenuation, this.sizeAttenuation);

        for (let i = 0; i < this.buffers.length; i++) {
            if (!this.buffers[i].texture) continue;
            gl.uniform2f(this.uniformLocation.u_Size, this.buffers[i].size[0], this.buffers[i].size[1]);
            gl.uniform2f(this.uniformLocation.u_Offset, this.buffers[i].offset[0], this.buffers[i].offset[1]);
            let move = null;
            let isShow = 0;
            const start = this.buffers[i].start;
            if (this.count >= this.buffers[i].startSeq && this.buffers[i].path[parseInt(this.count - this.buffers[i].startSeq) * 3 + 2]) {
                // move = [start.x - this.buffers[i].path[parseInt(this.count) * 3], start.y - this.buffers[i].path[parseInt(this.count) * 3 + 1], start.z - this.buffers[i].path[parseInt(this.count) * 3 + 2]];
                move = [start.x - this.buffers[i].path[parseInt(this.count - this.buffers[i].startSeq) * 3], start.y - this.buffers[i].path[parseInt(this.count - this.buffers[i].startSeq) * 3 + 1], start.z - this.buffers[i].path[parseInt(this.count - this.buffers[i].startSeq) * 3 + 2]];
                isShow = 1;
            }
            else {
                move = [0, 0, 0];
                isShow = 0;
            }
            gl.uniform3fv(this.uniformLocation.u_Move, move);
            gl.uniform1f(this.uniformLocation.u_IsShow, isShow);

            gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers[i].positionVertexBuffer);
            gl.vertexAttribPointer(this.attribLocation.a_Position, 3, gl.FLOAT, false, 0, 0);
            gl.enableVertexAttribArray(this.attribLocation.a_Position);

            gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers[i].texCoordVertexBuffer);
            gl.vertexAttribPointer(this.attribLocation.a_TexCoord, 2, gl.FLOAT, false, 0, 0);
            gl.enableVertexAttribArray(this.attribLocation.a_TexCoord);

            gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers[i].sideCoordVertexBuffer);
            gl.vertexAttribPointer(this.attribLocation.a_SideCoord, 2, gl.FLOAT, false, 0, 0);
            gl.enableVertexAttribArray(this.attribLocation.a_SideCoord);

            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffers[i].indexBuffer);

            gl.activeTexture(gl.TEXTURE0);
            gl.bindTexture(gl.TEXTURE_2D, this.buffers[i].texture);

            gl.drawElements(gl.TRIANGLES, this.buffers[i].n, gl.UNSIGNED_SHORT, 0);
        }

        // gl.enable(gl.DEPTH_TEST);
        gl.disable(gl.BLEND);
    }
}

// Puts text in center of canvas.
function makeTextCanvas(text, width, height, fontSize) {
    const textCtx = document.createElement("canvas").getContext("2d");
    textCtx.canvas.width = width;
    textCtx.canvas.height = height;
    textCtx.font = fontSize * 2 + "px Arial";
    textCtx.textAlign = "center";
    textCtx.textBaseline = "middle";
    textCtx.fillStyle = "white";
    textCtx.clearRect(0, 0, textCtx.canvas.width, textCtx.canvas.height);
    textCtx.fillText(text, width / 2, height / 2);
    return textCtx.canvas;
}

// Puts image in center of canvas
function makeImageCanvas(imageUrl, width, height, gl, buffers, i) {
    const imgCtx = document.createElement("canvas").getContext("2d");
    imgCtx.canvas.width = width;
    imgCtx.canvas.height = height;
    imgCtx.clearRect(0, 0, imgCtx.canvas.width, imgCtx.canvas.height);
    const imgObj = new Image();
    imgObj.src = imageUrl;
    imgObj.width = width;
    imgObj.height = height;
    imgObj.onload = function () {
        imgCtx.drawImage(imgObj, 0, 0);
        const textImg = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, textImg);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, imgCtx.canvas);
        // make sure we can render it even if it's not a power of 2
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        buffers[i].texture = textImg;
    }
}

        // var labelModelMatrix = mat4.create();
        // mat4.copy(labelModelMatrix, modelMatrix);
        // mat4.translate(labelModelMatrix, labelModelMatrix, [0, 0, this.radius]);
        // mat4.rotateX(labelModelMatrix, labelModelMatrix, -currentAngle[0] / 180 * Math.PI);
        // mat4.rotateY(labelModelMatrix, labelModelMatrix, -currentAngle[1] / 180 * Math.PI);
        // mat4.translate(labelModelMatrix, labelModelMatrix, [0, 0, -this.radius]);

        // var mvp = mat4.create();
        // mat4.copy(mvp, projMatrix);
        // mat4.multiply(mvp, mvp, viewMatrix);
        // mat4.multiply(mvp, mvp, modelMatrix);
        // var pos = [0.0, 0.0, 2.0111, 1.0];
        // var posNow = vec4.create();
        // vec4.transformMat4(posNow, pos, mvp);
        // console.log(viewMatrix[14], posNow[3]);

        // // 在屏幕上固定像素距离
        // var pos = [0.0, 0.0, 2.0111];
        // var posNow = vec3.create();
        // vec3.transformMat4(posNow, pos, labelModelMatrix);
        // var desiredTextScale = 1 / gl.canvas.height / projMatrix[0];  // 1x1 pixels
        // var scale = (-viewMatrix[14] - posNow[2]) * desiredTextScale * 100;
        // mat4.scale(labelModelMatrix, labelModelMatrix, [scale, scale, 1]);
